#include "wxseventseditor.h"
#include "wxsitem.h"
#include "wxsitemresdata.h"
#include "../wxscoder.h"
#include <editormanager.h>
#include "M_Code_StyledTextCtrl.h"

namespace {
  const wxString NoneStr   = _T( "-- None --" );
  const wxString AddNewStr = _T( "-- Add new handler --" );
}

wxsEventsEditor::wxsEventsEditor():
  m_Item( 0 ),
  m_Events( 0 ),
  m_Source(),
  m_Header(),
  m_Class(),
  m_Ids() {
}

wxsEventsEditor::~wxsEventsEditor() {
  m_Item = 0;
  m_Events = 0;
  m_Ids.Clear();
}

wxsEventsEditor& wxsEventsEditor::Get() {
  static wxsEventsEditor Singleton;
  return Singleton;
}

void wxsEventsEditor::BuildEvents( wxsItem* Item, wxsPropertyGridManager* Grid ) {
  m_Item = Item;
  m_Data = 0;
  m_Events = 0;
  m_Ids.Clear();
  m_Source.Clear();
  m_Header.Clear();
  m_Class.Clear();
  int PageIndex = 1;
  Grid->ClearPage( PageIndex );
  Grid->SelectPage( PageIndex );
  if( !m_Item ) {
    return;
  }
  m_Events = &m_Item->GetEvents();
  m_Data   = m_Item->GetResourceData();
  m_Source = m_Data->GetSrcFileName();
  m_Header = m_Data->GetHdrFileName();
  m_Class  = m_Data->GetClassName();
  m_Language = m_Data->GetLanguage();
  int Cnt = m_Events->GetCount();
  for( int i = 0; i < Cnt; i++ ) {
    const wxsEventDesc* Event = m_Events->GetDesc( i );
    const wxString& FunctionName = m_Events->GetHandler( i );
    if( Event->ET == wxsEventDesc::Category ) {
      m_Ids.Add( 0 );
      continue;
    }
    wxArrayString Functions;
    FindFunctions( Event->ArgType, Functions );
    wxPGChoices Const;
    Const.Add( NoneStr, 0 );
    Const.Add( AddNewStr, 1 );
    int Index = 0;
    for( int j = 0; j < ( int )Functions.Count(); j++ ) {
      Const.Add( Functions[j], j + 2 );
      if( Functions[j] == FunctionName ) {
        Index = j + 2;
      }
    }
    if( Index == 0 ) {
      m_Events->SetHandler( i, _T( "" ) );
    }
    m_Ids.Add( Grid->Append( new wxEnumProperty( Event->Entry, wxPG_LABEL, Const, Index ) ) );
  }
  Grid->SelectPage( 0 );
}

void wxsEventsEditor::PGChanged( wxsItem* Item, wxsPropertyGridManager* Grid, wxPGId Id ) {
  if( Item != m_Item ) {
    return;
  }
  int Index;
  for( Index = 0; Index < ( int )m_Ids.Count(); Index++ ) {
    if( m_Ids[Index] == Id ) {
      break;
    }
  }
  if( Index >= ( int )m_Ids.Count() ) {
    return;
  }
  wxString Selection = Grid->GetPropertyValueAsString( Id );
  wxString usedHandlerName = m_Events->GetHandler( Index );
  if( ( !usedHandlerName.IsEmpty() )
      && usedHandlerName.IsSameAs( Selection ) ) {
    GotoHandler( Index );
    return;
  } else if( usedHandlerName.IsEmpty()
             && Selection.IsSameAs( NoneStr ) ) {
    return;
  }
  if( Selection == NoneStr ) {
    m_Events->SetHandler( Index, _T( "" ) );
  } else if( Selection == AddNewStr ) {
    m_Events->SetHandler( Index, GetNewFunction( m_Events->GetDesc( Index ) ) );
    BuildEvents( m_Item, Grid );
  } else {
    m_Events->SetHandler( Index, Selection );
    GotoHandler( Index );
  }
  m_Data->NotifyChange( m_Item );
}

bool wxsEventsEditor::GotoOrBuildEvent( wxsItem* Item, int EventIndex, wxsPropertyGridManager* Grid ) {
  if( Item != m_Item ) {
    return false;
  }
  if( EventIndex < 0 ) {
    return false;
  }
  if( EventIndex >= m_Events->GetCount() ) {
    return false;
  }
  if( m_Events->GetHandler( EventIndex ).IsEmpty() ) {
    wxString NewFunctionName = GetFunctionProposition( m_Events->GetDesc( EventIndex ) );
    if( CreateNewFunction( m_Events->GetDesc( EventIndex ), NewFunctionName ) ) {
      m_Events->SetHandler( EventIndex, NewFunctionName );
      BuildEvents( Item, Grid );
      m_Data->NotifyChange( m_Item );
      return true;
    }
    return false;
  } else {
    GotoHandler( EventIndex );
    return false;
  }
}

void wxsEventsEditor::FindFunctions( const wxString& ArgType, wxArrayString& Array ) {
  wxString Code = wxsCoder::Get()->GetCode( m_Header,
                  wxsCodeMarks::Beg( m_Language, _T( "Handlers" ), m_Class ),
                  wxsCodeMarks::End( m_Language ),
                  false, false );
  switch( m_Language ) {
    case wxsCPP: {
      for( ;; ) {
        int Pos = Code.Find( _T( "void" ) );
        if( Pos == -1 ) {
          break;
        }
        Code.Remove( 0, Pos + 4 ).Trim( false );
        Pos  = 0;
        while( ( int )Code.Length() > Pos ) {
          wxChar First = Code.GetChar( Pos );
          if( ( First < _T( 'A' ) || First > _T( 'Z' ) ) &&
              ( First < _T( 'a' ) || First > _T( 'z' ) ) &&
              ( First < _T( '0' ) || First > _T( '9' ) ) &&
              ( First != _T( '_' ) ) ) {
            break;
          }
          Pos++;
        }
        wxString NewFunctionName = Code.Mid( 0, Pos );
        Code.Remove( 0, Pos ).Trim( false );
        if( !Code.Length() ) {
          break;
        }
        if( Code.GetChar( 0 ) != _T( '(' ) ) {
          continue;
        }
        Code.Remove( 0, 1 ).Trim( false );
        if( !Code.Length() ) {
          break;
        }
        Pos  = 0;
        while( ( int )Code.Length() > Pos ) {
          wxChar First = Code.GetChar( Pos );
          if( ( First < _T( 'A' ) || First > _T( 'Z' ) ) &&
              ( First < _T( 'a' ) || First > _T( 'z' ) ) &&
              ( First < _T( '0' ) || First > _T( '9' ) ) &&
              ( First != _T( '_' ) ) ) {
            break;
          }
          Pos++;
        }
        wxString NewEventType = Code.Mid( 0, Pos );
        Code.Remove( 0, Pos ).Trim( false );
        if( !Code.Length() ) {
          break;
        }
        if( Code.GetChar( 0 ) != _T( '&' ) ) {
          continue;
        }
        Code.Remove( 0, 1 ).Trim( false );
        if( !Code.Length() ) {
          break;
        }
        Pos  = 0;
        while( ( int )Code.Length() > Pos ) {
          wxChar First = Code.GetChar( Pos );
          if( ( First < _T( 'A' ) || First > _T( 'Z' ) ) &&
              ( First < _T( 'a' ) || First > _T( 'z' ) ) &&
              ( First < _T( '0' ) || First > _T( '9' ) ) &&
              ( First != _T( '_' ) ) ) {
            break;
          }
          Pos++;
        }
        Code.Remove( 0, Pos ).Trim( false );
        if( !Code.Length() ) {
          break;
        }
        if( Code.GetChar( 0 ) != _T( ')' ) ) {
          continue;
        }
        Code.Remove( 0, 1 ).Trim( false );
        if( !Code.Length() ) {
          break;
        }
        if( Code.GetChar( 0 ) != _T( ';' ) ) {
          continue;
        }
        Code.Remove( 0, 1 ).Trim( false );
        if( NewFunctionName.Length() == 0 || NewEventType.Length() == 0 ) {
          continue;
        }
        if( !ArgType.Length() || ArgType == NewEventType ) {
          Array.Add( NewFunctionName );
        }
      }
      break;
    }
    case wxsUnknownLanguage:
    default: {
      wxsCodeMarks::Unknown( _T( "wxsEventsEditor::FindFunctions" ), m_Language );
    }
  }
}

wxString wxsEventsEditor::GetFunctionProposition( const wxsEventDesc* Event ) {
  wxString NewNameBase;
  wxString VarName = m_Item->IsRootItem() ? _T( "" ) : m_Item->GetVarName();
  if( Manager::Get()->GetConfigManager( _T( "wxsmith" ) )->ReadBool( _T( "/removeprefix" ), false ) ) {
    if( VarName.StartsWith( _T( "m_" ) ) ) {
      VarName = VarName.Right( VarName.Len() - 2 );
    } else if( VarName.StartsWith( _T( "_" ) ) ) {
      VarName = VarName.Right( VarName.Len() - 1 );
    }
  }
  NewNameBase.Printf( _T( "On%s%s" ), VarName.c_str(), Event->NewFuncNameBase.c_str() );
  int Suffix = 0;
  wxArrayString Functions;
  FindFunctions( _T( "" ), Functions );
  wxString NewName = NewNameBase;
  while( Functions.Index( NewName ) != wxNOT_FOUND ) {
    NewName = NewNameBase;
    NewName.Append( wxString::Format( _T( "%d" ), ++Suffix ) );
  }
  return NewName;
}

wxString wxsEventsEditor::GetNewFunction( const wxsEventDesc* Event ) {
  wxString Name = GetFunctionProposition( Event );
  for( ;; ) {
    Name = ::cbGetTextFromUser( _T( "输入新处理程序的名称:" ), _T( "New handler" ), Name );
    if( !Name.Length() ) {
      return _T( "" );
    }
    if( !wxsCodeMarks::ValidateIdentifier( m_Language, Name ) ) {
      wxMessageBox( _T( "Invalid name" ) );
      continue;
    }
    wxArrayString Functions;
    FindFunctions( _T( "" ), Functions );
    if( Functions.Index( Name ) != wxNOT_FOUND ) {
      wxMessageBox( _T( "具有此名称的处理程序已存在" ) );
      continue;
    }
    break;
  }
  if( !CreateNewFunction( Event, Name ) ) {
    wxMessageBox( _T( "无法添加新的处理程序" ) );
    return _T( "" );
  }
  return Name;
}

bool wxsEventsEditor::CreateNewFunction( const wxsEventDesc* Event, const wxString& NewFunctionName ) {
  switch( m_Language ) {
    case wxsCPP: {
      wxString Declarations = wxsCoder::Get()->GetCode(
                                m_Header,
                                wxsCodeMarks::Beg( wxsCPP, _T( "Handlers" ), m_Class ),
                                wxsCodeMarks::End( wxsCPP ),
                                false, false );
      if( Declarations.Length() == 0 ) {
        return false;
      }
      Declarations << _T( "void " ) << NewFunctionName << _T( '(' );
      Declarations << Event->ArgType << _T( "& event);\n" );
      wxsCoder::Get()->AddCode(
        m_Header,
        wxsCodeMarks::Beg( wxsCPP, _T( "Handlers" ), m_Class ),
        wxsCodeMarks::End( wxsCPP ),
        Declarations,
        true, false, false );
      M_Code_Editor* Editor = Manager::Get()->GetEditorManager()->Open( m_Source );
      if( !Editor ) {
        return false;
      }
      wxString NewFunctionCode;
      NewFunctionCode <<
                      _T( "\n" )
                      _T( "void " ) << m_Class << _T( "::" ) << NewFunctionName << _T( "(" ) << Event->ArgType << _T( "& event)\n" )
                      _T( "{\n" )
                      _T( "}\n" );
      M_Code_StyledTextCtrl* Ctrl = Editor->GetControl();
      int LineNumber = Ctrl->GetLineCount();
      Ctrl->DocumentEnd();
      Ctrl->AddText( NewFunctionCode );
      Editor->SetModified();
      Editor->Activate();
      Editor->GotoLine( LineNumber + 2 );
      Ctrl->LineEnd();
      return true;
    }
    case wxsUnknownLanguage:
    default: {
      wxsCodeMarks::Unknown( _T( "wxsEventsEditor::CreateNewFunction" ), m_Language );
    }
  }
  return true;
}

bool wxsEventsEditor::GotoHandler( int Index ) {
  M_Code_Editor* Editor = Manager::Get()->GetEditorManager()->Open( m_Source );
  if( !Editor ) {
    return false;
  }
  wxString FunctionName = m_Events->GetHandler( Index );
  if( FunctionName.IsEmpty() ) {
    return false;
  }
  M_Code_StyledTextCtrl* Ctrl = Editor->GetControl();
  wxString FullText = Ctrl->GetText();
  int Begin = 0;
  int End = Ctrl->GetLength();
  while( Begin < End ) {
    int Pos = Ctrl->FindText( Begin, End, FunctionName, wxSCI_FIND_MATCHCASE );
    if( Pos < 0 ) {
      break;
    }
    int Before = Pos;
    while( --Before >= 0 ) {
      wxChar Ch = Ctrl->GetCharAt( Before );
      if( Ch != ' ' && Ch != '\t' && Ch != '\r' && Ch != '\n' ) {
        break;
      }
    }
    if( Before >= 1 ) {
      if( Ctrl->GetCharAt( Before ) == ':' && Ctrl->GetCharAt( Before - 1 ) == ':' ) {
        Before--;
        while( --Before >= 0 ) {
          wxChar Ch = Ctrl->GetCharAt( Before );
          if( Ch != ' ' && Ch != '\t' && Ch != '\r' && Ch != '\n' ) {
            break;
          }
        }
        if( Before > 0 ) {
          wxString ClassName;
          while( Before >= 0 ) {
            wxChar Ch = Ctrl->GetCharAt( Before-- );
            if( ( Ch < 'a' || Ch > 'z' ) && ( Ch < 'A' || Ch > 'Z' ) && ( Ch < '0' || Ch > '9' ) && ( Ch != '_' ) )
            { break; }
            ClassName = Ch + ClassName;
          }
          if( ClassName == m_Class ) {
            int After = Pos + FunctionName.Length();
            while( After < End ) {
              wxChar Ch = Ctrl->GetCharAt( After );
              if( Ch != ' ' && Ch != '\t' && Ch != '\r' && Ch != '\n' )
              { break; }
              After++;
            }
            if( After < End ) {
              if( Ctrl->GetCharAt( After ) == '(' ) {
                Editor->GotoLine( Ctrl->LineFromPosition( Pos ) );
                return true;
              }
            }
          }
        }
      }
    }
    Begin = Pos + FunctionName.Length();
  }
  return false;
}
